/*
 * Copyright (C) 2019 ByteDance Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bytedance.scene.animation;

import com.bytedance.scene.animation.interaction.ghostview.GhostViewUtils;
import com.bytedance.scene.utlity.NonNullPair;
import ohos.agp.components.Attr;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentParent;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.media.camera.params.FocusResult;
import ohos.utils.Pair;

import java.util.*;

public class SharedElementUtils {
    private static WeakHashMap<Component, String> sTransitionNameMap;

    /**
     * RootView must be excluded, The rootView does an alpha animation equal to the system's captureTransitioningViews.
     *
     * @param view Component
     * @param rootView Component
     * @return List
     */
    public static List<Component> captureTransitioningViews(Component view, Component rootView) {
        List<Component> list = new ArrayList<>();
        captureTransitioningViews(view, rootView, list);
        return list;
    }

    private static void captureTransitioningViews(Component view, Component rootView, List<Component> list) {
        if (view.getVisibility() != Component.VISIBLE) {
            return;
        }
        if (view instanceof ComponentContainer) {
            ComponentContainer viewGroup = (ComponentContainer) view;
            if (view != rootView) {
                list.add(viewGroup);
            } else {
                for (int i = 0; i < viewGroup.getChildCount(); i++) {
                    captureTransitioningViews(viewGroup.getComponentAt(i), rootView, list);
                }
            }
        } else {
            if (view.getVisibility() == Component.VISIBLE) {
                list.add(view);
            }
        }
    }

    public static void moveViewToOverlay(List<Component> viewList, ComponentContainer dstViewGroup) {
        for (Component view : viewList) {
            GhostViewUtils.addGhost(view, dstViewGroup, null);
        }
    }

    public static void moveViewFromOverlay(List<Component> viewList) {
        for (Component view : viewList) {
            GhostViewUtils.removeGhost(view);
        }
    }

    public static void moveViewToOverlay(Component view, ComponentContainer dstViewGroup, Matrix matrix) {
        GhostViewUtils.addGhost(view, dstViewGroup, matrix);
    }

    public static void moveViewFromOverlay(Component view) {
        GhostViewUtils.removeGhost(view);
    }

    public static Component getViewByTransitionName(Component view, String transitionName, boolean visible) {
        if (transitionName.equals(getTransitionName(view))) {
            return view;
        } else if (view instanceof ComponentContainer) {
            for (int i = 0; i < ((ComponentContainer) view).getChildCount(); i++) {
                Component childView = ((ComponentContainer) view).getComponentAt(i);
                if (childView.getVisibility() == Component.VISIBLE || !visible) {
                    Component targetView = getViewByTransitionName(childView, transitionName, visible);
                    if (targetView != null) {
                        return targetView;
                    }
                }
            }
        }
        return null;
    }

    private static HashMap<String, Component> getTransitionViewList(Component view, List<String> transitionNameList, boolean visible) {
        transitionNameList = new ArrayList<>(new HashSet<>(transitionNameList));
        HashMap<String, Component> hashMap = new HashMap<>();
        for (String transitionName : transitionNameList) {
            Component targetView = SharedElementUtils.getViewByTransitionName(view, transitionName, visible);
            if (targetView != null) {
                hashMap.put(transitionName, targetView);
            }
        }
        return hashMap;
    }

    private static HashMap<String, Pair<Component, Component>> getShareView(Component fromView, Component toView, List<String> share) {
        HashMap<String, Component> fromList = getTransitionViewList(fromView, share, true);
        HashMap<String, Component> toList = getTransitionViewList(toView, share, true);

        HashMap<String, Pair<Component, Component>> hashMap = new HashMap<>();
        for (Map.Entry<String, Component> entry : fromList.entrySet()) {
            String key = entry.getKey();
            Component toTargetView = toList.get(key);
            if (toTargetView != null) {
                hashMap.put(key, Pair.create(entry.getValue(), toTargetView));
            }
        }
        return hashMap;
    }

    private static NonNullPair<Component, Component> getShareView(Component fromView, Component toView, String share) {
        return NonNullPair.create(SharedElementUtils.getViewByTransitionName(fromView, share, true), SharedElementUtils.getViewByTransitionName(toView, share, true));
    }

    public static NonNullPair<List<Component>, List<Component>> stripOffscreenViews(List<Component> viewList) {
        List<Component> transitioningViews = new ArrayList<>(viewList);
        List<Component> strippedTransitioningViews = new ArrayList<>();
        Rect r = new Rect();
        for (int i = transitioningViews.size() - 1; i >= 0; i--) {
            Component view = transitioningViews.get(i);
            if (!view.getSelfVisibleRect(r)) {
                transitioningViews.remove(i);
                strippedTransitioningViews.add(view);
            }
        }
        return NonNullPair.create(transitioningViews, strippedTransitioningViews);
    }

    /**
     * Guarantee order: Parent -> Child
     * Make sure that Parent will not overwrite Child when adding Overlay
     *
     * @param sharedElements HashMap
     * @return List
     */
    public static List<NonNullPair<String, Component>> sortSharedElementList(HashMap<String, Component> sharedElements) {
        List<NonNullPair<String, Component>> list = new ArrayList<>();
        boolean isFirstRun = true;
        while (!sharedElements.isEmpty()) {
            Set<Map.Entry<String, Component>> entrySet = sharedElements.entrySet();
            for (Map.Entry<String, Component> map : entrySet) {
                final Component view = map.getValue();
                final String name = map.getKey();
                if (isFirstRun && (view == null || !view.isBoundToWindow() || name == null)) {
                    sharedElements.remove(name);
                } else if (!isNested(view, sharedElements)) {
                    list.add(NonNullPair.create(name, view));
                    sharedElements.remove(name);
                }
            }
            isFirstRun = false;
        }
        return list;
    }

    private static boolean isNested(Component view, HashMap<String, Component> sharedElements) {
        ComponentParent parent = view.getComponentParent();
        boolean isNested = false;
        while (parent instanceof Component) {
            Component parentView = (Component) parent;
            if (sharedElements.containsValue(parentView)) {
                isNested = true;
                break;
            }
            parent = parentView.getComponentParent();
        }
        return isNested;
    }

    public static void setTransitionName(Component view, String transitionName) {
        if (sTransitionNameMap == null) {
            sTransitionNameMap = new WeakHashMap<>();
        }
        sTransitionNameMap.put(view, transitionName);
    }

    public static String getTransitionName(Component view) {
        if (sTransitionNameMap == null) {
            return null;
        }
        return sTransitionNameMap.get(view);
    }
}
